from flask import Blueprint, request, jsonify, current_app, g
from flask_jwt_extended import create_access_token, create_refresh_token, jwt_required, get_jwt_identity, get_jwt
from datetime import datetime, timedelta
import uuid
import redis
import json

from models import db, User, WechatAccount, SmsCode, WechatQrCode, LoginLog, RefreshToken
from utils import EncryptionUtil, SmsUtil, WechatUtil, QRCodeUtil, get_client_ip, validate_phone, validate_email
from security_middleware import rate_limit, validate_input
from security_enhanced import enhanced_rate_limit, enhanced_validate_input

auth_bp = Blueprint('auth', __name__)

def get_redis_client():
    """获取Redis客户端"""
    return redis.from_url(current_app.config.get('REDIS_URL', 'redis://localhost:6379/0'))

def create_response(success=True, message="", data=None, code=200):
    """创建统一的响应格式"""
    response_data = {
        'success': success,
        'message': message,
        'data': data,
        'timestamp': datetime.utcnow().isoformat()
    }
    
    return jsonify(response_data), code

def get_request_data():
    """获取解密后的请求数据"""
    return getattr(g, 'decrypted_data', {})

def log_login_attempt(user_id, login_type, success=True, failure_reason=None):
    """记录登录日志"""
    try:
        log = LoginLog(
            user_id=user_id,
            login_type=login_type,
            ip_address=get_client_ip(request),
            user_agent=request.headers.get('User-Agent'),
            success=success,
            failure_reason=failure_reason
        )
        db.session.add(log)
        db.session.commit()
    except Exception as e:
        print(f"记录登录日志失败: {str(e)}")

@auth_bp.route('/login', methods=['POST'])
@enhanced_rate_limit('login')  # 使用增强的频率限制
@enhanced_validate_input()     # 使用增强的输入验证
def login():
    """账号密码登录"""
    data = get_request_data()
    if not data:
        return create_response(False, "请求数据格式错误", code=400)
    
    username = data.get('username')
    password = data.get('password')
    
    if not username or not password:
        return create_response(False, "用户名和密码不能为空", code=400)
    
    # 查找用户（支持用户名、邮箱、手机号登录）
    user = None
    if validate_email(username):
        user = User.query.filter_by(email=username).first()
    elif validate_phone(username):
        user = User.query.filter_by(phone=username).first()
    else:
        user = User.query.filter_by(username=username).first()
    
    if not user or not user.check_password(password):
        if user:
            log_login_attempt(user.id, 'password', False, '密码错误')
        return create_response(False, "用户名或密码错误", code=401)
    
    if user.status != 1:
        log_login_attempt(user.id, 'password', False, '账号已禁用')
        return create_response(False, "账号已被禁用", code=403)
    
    # 更新最后登录时间
    user.last_login_at = datetime.utcnow()
    db.session.commit()
    
    # 创建JWT令牌
    access_token = create_access_token(identity=user.uuid)
    refresh_token_str = create_refresh_token(identity=user.uuid)
    
    # 保存刷新令牌
    refresh_token = RefreshToken(
        user_id=user.id,
        token=refresh_token_str,
        expires_at=datetime.utcnow() + current_app.config['JWT_REFRESH_TOKEN_EXPIRES']
    )
    db.session.add(refresh_token)
    db.session.commit()
    
    # 记录登录日志
    log_login_attempt(user.id, 'password', True)
    
    return create_response(True, "登录成功", {
        'user': user.to_dict(),
        'access_token': access_token,
        'refresh_token': refresh_token_str
    })

@auth_bp.route('/sms-login', methods=['POST'])
@enhanced_rate_limit('sms_login')  # 使用增强的频率限制
@enhanced_validate_input()         # 使用增强的输入验证
def sms_login():
    """短信验证码登录"""
    data = get_request_data()
    if not data:
        return create_response(False, "请求数据格式错误", code=400)
    
    phone = data.get('phone')
    code = data.get('code')
    
    if not phone or not code:
        return create_response(False, "手机号和验证码不能为空", code=400)
    
    if not validate_phone(phone):
        return create_response(False, "手机号格式不正确", code=400)
    
    # 验证短信验证码
    sms_code = SmsCode.query.filter_by(
        phone=phone,
        purpose='login'
    ).order_by(SmsCode.created_at.desc()).first()
    
    if not sms_code or not sms_code.is_valid(code):
        return create_response(False, "验证码错误或已过期", code=400)
    
    # 标记验证码为已使用
    sms_code.used = True
    
    # 查找或创建用户
    user = User.query.filter_by(phone=phone).first()
    if not user:
        user = User(
            uuid=str(uuid.uuid4()),
            phone=phone,
            nickname=f"用户{phone[-4:]}"
        )
        db.session.add(user)
        db.session.flush()  # 获取用户ID
    
    if user.status != 1:
        log_login_attempt(user.id, 'sms', False, '账号已禁用')
        return create_response(False, "账号已被禁用", code=403)
    
    # 更新最后登录时间
    user.last_login_at = datetime.utcnow()
    sms_code.user_id = user.id
    db.session.commit()
    
    # 创建JWT令牌
    access_token = create_access_token(identity=user.uuid)
    refresh_token_str = create_refresh_token(identity=user.uuid)
    
    # 保存刷新令牌
    refresh_token = RefreshToken(
        user_id=user.id,
        token=refresh_token_str,
        expires_at=datetime.utcnow() + current_app.config['JWT_REFRESH_TOKEN_EXPIRES']
    )
    db.session.add(refresh_token)
    db.session.commit()
    
    # 记录登录日志
    log_login_attempt(user.id, 'sms', True)
    
    return create_response(True, "登录成功", {
        'user': user.to_dict(),
        'access_token': access_token,
        'refresh_token': refresh_token_str
    })

@auth_bp.route('/send-sms', methods=['POST'])
@enhanced_rate_limit('send_sms')  # 使用增强的频率限制
@enhanced_validate_input()        # 使用增强的输入验证
def send_sms():
    """发送短信验证码"""
    data = get_request_data()
    if not data:
        return create_response(False, "请求数据格式错误", code=400)
    
    phone = data.get('phone')
    purpose = data.get('purpose', 'login')  # login, register
    
    if not phone:
        return create_response(False, "手机号不能为空", code=400)
    
    if not validate_phone(phone):
        return create_response(False, "手机号格式不正确", code=400)
    
    # 检查发送频率限制（1分钟内只能发送一次）
    recent_sms = SmsCode.query.filter_by(phone=phone).filter(
        SmsCode.created_at > datetime.utcnow() - timedelta(minutes=1)
    ).first()
    
    if recent_sms:
        return create_response(False, "发送过于频繁，请稍后再试", code=429)
    
    # 生成验证码
    sms_util = SmsUtil(
        current_app.config.get('TENCENT_SMS_SECRET_ID'),
        current_app.config.get('TENCENT_SMS_SECRET_KEY'),
        current_app.config.get('TENCENT_SMS_APP_ID'),
        current_app.config.get('TENCENT_SMS_SIGN_NAME'),
        current_app.config.get('TENCENT_SMS_TEMPLATE_ID'),
        current_app.config.get('TENCENT_SMS_REGION', 'ap-beijing')
    )
    
    code = sms_util.generate_code()
    
    # 发送短信
    success, message = sms_util.send_sms(phone, code)
    
    if not success:
        return create_response(False, f"短信发送失败: {message}", code=500)
    
    # 保存验证码
    sms_code = SmsCode(
        phone=phone,
        code=code,
        purpose=purpose,
        expires_at=datetime.utcnow() + timedelta(minutes=5)
    )
    db.session.add(sms_code)
    db.session.commit()
    
    return create_response(True, "验证码发送成功")

@auth_bp.route('/wechat-qr', methods=['POST'])
def wechat_qr():
    """获取微信扫码登录二维码"""
    try:
        # 生成场景值
        scene_id = str(uuid.uuid4())
        
        # 创建微信工具实例
        wechat_util = WechatUtil(
            current_app.config.get('WECHAT_APP_ID'),
            current_app.config.get('WECHAT_APP_SECRET'),
            current_app.config.get('WECHAT_TOKEN')
        )
        
        # 创建二维码
        ticket, qr_url = wechat_util.create_qr_code(scene_id, 600)  # 10分钟有效期
        
        # 保存二维码信息
        qr_code = WechatQrCode(
            scene_id=scene_id,
            ticket=ticket,
            qr_code_url=qr_url,
            expires_at=datetime.utcnow() + timedelta(minutes=10)
        )
        db.session.add(qr_code)
        db.session.commit()
        
        return create_response(True, "二维码生成成功", {
            'scene_id': scene_id,
            'qr_code_url': qr_url,
            'expires_at': qr_code.expires_at.isoformat()
        })
        
    except Exception as e:
        return create_response(False, f"生成二维码失败: {str(e)}", code=500)

@auth_bp.route('/wechat-check', methods=['POST'])
def wechat_check():
    """检查微信扫码状态"""
    data = get_request_data()
    if not data:
        return create_response(False, "请求数据格式错误", code=400)
    
    scene_id = data.get('scene_id')
    if not scene_id:
        return create_response(False, "场景值不能为空", code=400)
    
    # 查找二维码记录
    qr_code = WechatQrCode.query.filter_by(scene_id=scene_id).first()
    if not qr_code:
        return create_response(False, "二维码不存在", code=404)
    
    if qr_code.is_expired():
        qr_code.status = 'expired'
        db.session.commit()
        return create_response(False, "二维码已过期", code=400)
    
    if qr_code.status == 'confirmed' and qr_code.user_id:
        # 登录成功，返回用户信息和令牌
        user = User.query.get(qr_code.user_id)
        if user and user.status == 1:
            # 更新最后登录时间
            user.last_login_at = datetime.utcnow()
            db.session.commit()
            
            # 创建JWT令牌
            access_token = create_access_token(identity=user.uuid)
            refresh_token_str = create_refresh_token(identity=user.uuid)
            
            # 保存刷新令牌
            refresh_token = RefreshToken(
                user_id=user.id,
                token=refresh_token_str,
                expires_at=datetime.utcnow() + current_app.config['JWT_REFRESH_TOKEN_EXPIRES']
            )
            db.session.add(refresh_token)
            db.session.commit()
            
            # 记录登录日志
            log_login_attempt(user.id, 'wechat', True)
            
            return create_response(True, "登录成功", {
                'user': user.to_dict(),
                'access_token': access_token,
                'refresh_token': refresh_token_str
            })
    
    return create_response(True, "等待扫码", {
        'status': qr_code.status
    })

@auth_bp.route('/refresh', methods=['POST'])
@jwt_required(refresh=True)
def refresh():
    """刷新访问令牌"""
    current_user_uuid = get_jwt_identity()
    user = User.query.filter_by(uuid=current_user_uuid).first()
    
    if not user or user.status != 1:
        return create_response(False, "用户不存在或已被禁用", code=401)
    
    # 创建新的访问令牌
    access_token = create_access_token(identity=user.uuid)
    
    return create_response(True, "令牌刷新成功", {
        'access_token': access_token
    })

@auth_bp.route('/logout', methods=['POST'])
@jwt_required()
def logout():
    """退出登录"""
    try:
        # 获取当前JWT令牌
        jti = get_jwt()['jti']
        
        # 将令牌加入黑名单（使用Redis）
        redis_client = get_redis_client()
        redis_client.set(f"blacklist:{jti}", "true", ex=current_app.config['JWT_ACCESS_TOKEN_EXPIRES'].total_seconds())
        
        return create_response(True, "退出登录成功")
    except Exception as e:
        return create_response(False, f"退出登录失败: {str(e)}", code=500)